/*
 * Copyright (C) 2007 Eskil Bylund
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Text;
using System.Xml;

namespace DCSharp.Logging
{
	public class ConfigurationSectionHandler : IConfigurationSectionHandler
	{
		public object Create(object parent, object context, XmlNode section)
		{
			LogSettings settings = new LogSettings();

			foreach (XmlNode node in section.SelectNodes("logger"))
			{
				XmlAttribute name = node.Attributes["name"];
				XmlAttribute level = node.Attributes["level"];

				if (name != null && level != null)
				{
					try
					{
						TraceLevel traceLevel = (TraceLevel)Enum.Parse(
							typeof(TraceLevel), level.InnerText);

						settings.Levels.Add(name.InnerText, traceLevel);
					}
					catch
					{
					}
				}
			}
			return settings;
		}
	}

	public class LogSettings
	{
		public Dictionary<string, TraceLevel> Levels;

		internal LogSettings()
		{
			Levels = new Dictionary<string, TraceLevel>();
		}

		public TraceLevel GetLevel(string name)
		{
			TraceLevel level;
			if (Levels.TryGetValue(name, out level) ||
				Levels.TryGetValue("Default", out level))
			{
				return level;
			}
			return TraceLevel.Warning;
		}
	}

	public delegate void LogHandler(Logger log, TraceLevel level, string message,
		Exception e, params object[] context);

	/// <summary>
	/// A class that handles logger instances.
	/// </summary>
	public static class LogManager
	{
		private static Dictionary<string, Logger> loggers;
		private static LogSettings settings;

		/// <summary>
		/// The handler of log messages.
		/// </summary>
		public static LogHandler Handlers;

		static LogManager()
		{
			loggers = new Dictionary<string, Logger>();

			settings = (LogSettings)ConfigurationManager.GetSection("logging");
			if (settings == null)
			{
				settings = new LogSettings();
			}
		}

		/// <summary>
		/// Gets the logger with the specified name.
		/// </summary>
		/// <param name="type">The name of the type will be used as the name.</param>
		/// <returns>The logger with the specified name.</returns>
		public static Logger GetLogger(Type type)
		{
			return GetLogger(type.Name);
		}

		/// <summary>
		/// Gets the logger with the specified name.
		/// </summary>
		/// <param name="name">The name of the logger.</param>
		/// <returns>The logger with the specified name.</returns>
		public static Logger GetLogger(string name)
		{
			lock (loggers)
			{
				Logger log;
				if (loggers.TryGetValue(name, out log))
				{
					return log;
				}

				log = new Logger(name);
				log.Level = settings.GetLevel(name);

				loggers.Add(name, log);

				return log;
			}
		}

		/// <summary>
		/// Creates a log message with the default format from a logging event.
		/// </summary>
		/// <param name="log">The logger that logged the message.</param>
		/// <param name="level">The log level of the message.</param>
		/// <param name="message">The log message.</param>
		/// <param name="e">The log exception.</param>
		/// <param name="context">Context to the log message.</param>
		/// <returns>A complete log message.</returns>
		public static string GetDefaultLogMessage(Logger log, TraceLevel level,
			string message, Exception e, params object[] context)
		{
			StringBuilder builder = new StringBuilder();
			message = message.TrimEnd('\n');

			// Create the log message
			builder.AppendFormat("{0} {1} {2} ", DateTime.Now, level, log.Name);
			if (context != null)
			{
				builder.Append("(");
				foreach (object o in context)
				{
					if (o != null)
					{
						builder.AppendFormat("{0} ", o.ToString());
					}
				}
				builder.Append(") ");
			}
			builder.AppendFormat("- {0}", message);

			// Append the exception
			if (e != null)
			{
				builder.AppendFormat("\n{0}", e);
			}
			return builder.ToString();
		}

		/// <summary>
		/// The default log message handler, writing error and warning messages
		/// to the error console and all other messages to the console.
		/// </summary>
		/// <param name="log">The logger the message came from.</param>
		/// <param name="level">The log level of the message.</param>
		/// <param name="message">The log message.</param>
		/// <param name="e">The exception causing the message.</param>
		/// <param name="context">Context to the log message.</param>
		public static void DefaultHandler(Logger log, TraceLevel level, string message,
			Exception e, params object[] context)
		{
			message = GetDefaultLogMessage(log, level, message, e, context);

			if (level == TraceLevel.Error || level == TraceLevel.Warning)
			{
				Console.Error.WriteLine(message);
			}
			else
			{
				Console.WriteLine(message);
			}
		}

		internal static void Log(Logger log, TraceLevel level, string message,
			Exception e, params object[] context)
		{
			if (Handlers != null)
			{
				Handlers(log, level, message, e, context);
			}
		}
	}
}
